package com.espirit.eap.sa;

import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.util.HashMap;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;

import com.espirit.eap.util.Functions;
import com.googlecode.cswish.Config;
import com.googlecode.cswish.exception.ApplicationException;
import com.googlecode.cswish.security.SafeManager;
import com.googlecode.cswish.struts.ContextHolder;
import com.googlecode.cswish.struts.Routing;
import com.googlecode.cswish.struts.hibernate.DynamicClassLoaderFactory;
import com.googlecode.cswish.struts.spring.ObjectFactory;
import com.googlecode.cswish.util.FrameConstant;

import freemarker.template.Template;

/**
 * @author Jerry.Feng Date: 2004-10-25 18:28:05
 * @version $id: $
 */
public class PermissionFilter implements Filter {
    /**
     * Logger for this class
     */
    private static final Logger logger = Logger.getLogger(PermissionFilter.class);
    
    private ServletContext servletContext;
    
    private WebsiteService websiteService;
    
    private DynamicClassLoaderFactory classLoaderFactory;
    
    private Template pageStatusTemplate;
    
    private Config config;
    
    /* 
     * @see javax.servlet.Filter#destroy()
     */
    public void destroy() {
    }

    /* 
     * @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
     */
    public void init(FilterConfig arg0) throws ServletException {
    	servletContext = arg0.getServletContext();
    	
    	websiteService = ObjectFactory.getInstance().getBean(WebsiteService.class, false);
    	classLoaderFactory = ObjectFactory.getInstance().getBean(DynamicClassLoaderFactory.class, false);
    	config = ObjectFactory.getInstance().getBean(Config.class, false);
    }

    /* 
     * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)
     */
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
        if (servletRequest instanceof HttpServletRequest) {
        	HttpServletRequest request = (HttpServletRequest)servletRequest;
        	String uri = Routing.getUri(request);
        	// TODO: check the permission of static resource? such as jsp resource
        	if (uri.startsWith("/static/")) {
        		chain.doFilter(servletRequest, servletResponse);
        		return ;
        	}
        	
       		logger.info(uri);
       		
        	String redirectLocation = null;
    		if (uri.length() == 0 || uri.charAt(uri.length() - 1) == '/') {
    			if (uri.length() <= 1) {
    				String siteName = websiteService.getDefaultWebsiteName();
    				String homePage = websiteService.getWebsite(siteName).getHomePage();
        			if (homePage != null && homePage.length() > 0) {
        				redirectLocation = homePage;
        			} else {
        				if (config.isEnableSitePath()) {
        					redirectLocation = '/' + siteName + "/index.html";
        				} else {
        					redirectLocation = "/index.html";
        				}
        			}
    			} else {
    				redirectLocation = uri + "index.html";
    			}
    		} else if ("/index.html".equals(uri)) {
    			String siteName = websiteService.getDefaultWebsiteName();
    			String homePage = websiteService.getWebsite(siteName).getHomePage();
    			if (homePage != null && homePage.length() > 0) {
    				redirectLocation = homePage;
    			} else {
    				if (config.isEnableSitePath()) {
    					redirectLocation = '/' + siteName + "/index.html";
    				} else {
    					redirectLocation = "/index.html";
    				}
    			}
    		}
    		if (redirectLocation != null) {
    			if (logger.isDebugEnabled()) {
    				logger.debug("Redirect path: " + redirectLocation);
    			}
//    			HttpServletResponse response = (HttpServletResponse) servletResponse;
//    			response.sendRedirect(request.getContextPath() + '/' + redirectLocation);
//    			return ;
    			request.setAttribute("javax.servlet.include.servlet_path", redirectLocation);
    			int index = redirectLocation.indexOf('?');
    			if (index > 0) {
    				// split the parameter to attribute
    				for (String kv : StringUtils.split(redirectLocation.substring(index + 1), '&')) {
    					int sep = kv.indexOf('=');
    					if (sep > 0) {
    						request.setAttribute(kv.substring(0, sep), kv.substring(sep + 1));
    					} else {
    						request.setAttribute(kv, "");
    					}
    				}
    				uri = redirectLocation.substring(0, index);
    			} else {
    				uri = redirectLocation;
    			}
    		}
    		// only check the action resource, see CSwishUnknownHandler.resultsByExtension
    		if (!uri.endsWith(".html") && !uri.endsWith(".jsp") && !uri.endsWith(".ftl")
    				|| uri.startsWith("/static/")) {
    			chain.doFilter(servletRequest, servletResponse);
    			return ;
    		}
    		
            ContextHolder context = ContextHolder.get();
        	try {
        		updateRequestSite(uri, context);
        		classLoaderFactory.bindSiteClassLoader();
        		
        		String siteName = context.getSiteName();
        		if (checkSiteReady(siteName)) {
        			// the path doesn't contain product name, set a default product name
        			if (context.getProductName() == null) {
        				Website website = websiteService.getWebsite(siteName);
        				if (website != null && website.getDefaultTemplate() != null) {
        					context.setProductName(website.getDefaultTemplate().getName());
        				} else {
        					// it's new product, the site name is the product name
        					context.setProductName(website.getProductName());
        				}
        			}
        			
        			// check the permission
        			boolean pass = SafeManager.getInstance().check(request, (HttpServletResponse)servletResponse);
        			if (pass) {
        				chain.doFilter(servletRequest, servletResponse);
        			} else {
        				String resultCode = request.getParameter(FrameConstant.PARAM_RESULTCODE);
        				if (resultCode != null) {
        					String result = null;
        					// TODO: more result
	        				if (FrameConstant.PARAM_RESULT_JSON.equals(resultCode)) {
	        					String loginPath = "";
	        					result = "{\"error\": \"NoPermission\",\"loginPath\": \"" + loginPath + "\"}";
	        				}
	        				
	        				if (result != null) {
		        				HttpServletResponse response = (HttpServletResponse)servletResponse;
		        				response.setContentType("text/html");
		        				response.setCharacterEncoding("utf8");
		        				response.getWriter().write(result);
		        				response.getWriter().close();
	        				}
        				}
        			}
        		} else {
        			outputPage((HttpServletResponse)servletResponse, servletContext, context.getSid());
        		}
        	} catch (ApplicationException ex) {
        		outputPage((HttpServletResponse)servletResponse, servletContext, context.getSid());
        	} finally {
        		if (logger.isDebugEnabled()) {
        			logger.debug(uri + " ==> END");
        		}
        		classLoaderFactory.unbindSiteClassLoader();
        		context.reset();
        	}
        }
    }
    
    private void updateRequestSite(String actionPath, ContextHolder context) {
		if (actionPath.length() < 2) {
			return ;
		}
		
		if (actionPath.charAt(0) == '/') {
			actionPath = actionPath.substring(1);		// skip the first '/'
		}
		int index = actionPath.indexOf('/');
		if (index > 0) {
			int begin = actionPath.startsWith("$/") ? 2 : 0;
			int end = begin;
			
			if (config.isEnableSitePath()) {
				end = actionPath.indexOf('/', begin);
				String siteName = actionPath.substring(begin, end);
				context.setSiteName(siteName);
			} else {
				String siteName = websiteService.getDefaultWebsiteName();
				context.setSiteName(siteName);
			}
			
			/*
			int productEnd = actionPath.indexOf('/', end + 1);
			if (productEnd > 0) {
				String productName = actionPath.substring(end + 1, productEnd);
				context.setProductName(productName);
			}
			*/
		} else {
			String siteName = websiteService.getDefaultWebsiteName();
			context.setSiteName(siteName);
			context.setProductName(siteName);
		}
	}
    
	private boolean checkSiteReady(String site) {
		Website webSite = websiteService.getWebsite(site);
		boolean active = false;
		if (webSite != null) {
			Integer status = webSite.getStatus();
			if (Website.ACTIVE_STATUS_STARTUP.equals(status) ) {
				active = true;
			} else if (Website.STATUS_INIT.equals(status)
					|| Website.STATUS_ACTIVE.equals(status) 
					|| Website.STATUS_STOP.equals(status)) {
				websiteService.startup(site);
				active = true;
			}
		} else {
			throw new ApplicationException("errors.invalidPage");
		}
		return active;
	}
	
	private void outputPage(HttpServletResponse response, ServletContext ctx, Integer sid) {
        try {
        	if (logger.isDebugEnabled()) {
        		logger.debug("Invalid site: " + sid.toString());
        	}
        	
        	if (pageStatusTemplate == null) {
	        	String tpl = "/view/website.status.ftl";
	        	InputStream input = null;
	        	try {
		        	input = this.getClass().getResourceAsStream(tpl);
		        	String content = IOUtils.toString(input, "utf-8");
		        	
		            pageStatusTemplate = new Template("status.ftl", new StringReader(content), null);
	        	} finally {
	        		IOUtils.closeQuietly(input);
	        	}
        	}
            
            HashMap<String,Object> data = new HashMap<String,Object>();
            Functions fn = ObjectFactory.getInstance().getBean(Functions.class, false);
            data.put("fn", fn);
            Website website = websiteService.getWebsite(sid.toString());
            if (website == null) {
            	website = new Website();
            	website.setCompId(sid);
            	website.setStatus(Website.STATUS_DISABLE);
            }
            data.put("website", website);
            
            Writer writer = new StringWriter();
            pageStatusTemplate.process(data, writer);

            response.setContentType("text/html");
            response.getWriter().write(writer.toString());
            response.getWriter().close();
        } catch (Exception exp) {
            if (logger.isDebugEnabled()) {
                logger.debug("Output Prepare Page", exp);
            }
        }
	}
}
